#pragma once

#include <functional>
#include "string.h"
#include "vector.h"
#include "map.h"

namespace dave = cpac;

namespace dave
{
    typedef std::function< void(vector< string >::type const&) > console_function;

    namespace console_functions
    {
        void default_function(vector< string >::type const& in_args);
        void inspect_variable(vector< string >::type const& in_args);
        void set_variable(cpac::vector< cpac::string >::type const& in_args);
        void quit(cpac::vector< cpac::string >::type const& in_args);
    }

    enum console_var_type
    {
        TYPE_BOOL,
        TYPE_UCHAR,
        TYPE_CHAR,
        TYPE_USHORT,
        TYPE_SHORT,
        TYPE_UINT,
        TYPE_INT,
        TYPE_ULONG,
        TYPE_LONG,
        TYPE_ULONGLONG,
        TYPE_LONGLONG,
        TYPE_FLOAT,
        TYPE_DOUBLE,
        TYPE_LONGDOUBLE
    };

    class console
    {
    public:
        console();
        console(console&& in_rhs);
        console& operator=(console&& in_rhs);

    private:
        console(console const& in_rhs);
        console& operator=(console const& in_rhs);

    private:
        struct variable
        {
            virtual ~variable() {}

            struct variable_concept
            {
                virtual ~variable_concept() {}
            };

            template< typename _Type >
            struct variable_model
                : variable_concept
            {
                variable_model(_Type& in_data)
                    : _data(in_data)
                {
                }

                //template< typename _Type >
                auto get_data() -> decltype(_data) { return _data; }
                _Type _data;
                console_var_type _type;
            };

            template< typename _Type >
            variable(_Type& in_variable)
            {
            }
        };

    public:
        virtual ~console();

    public:
        void add_function(string const& in_name, std::function< void(vector< string >::type const&) > in_function)
        {
            _functions[in_name] = in_function;
        }

        template< typename _Type >
        void add_variable(string const& in_name, _Type& in_value, console_var_type in_type)
        {
            _variables[in_name]._data = &in_value;
            _variables[in_name]._type = in_type;
        }

        void remove_item(string const& in_name);

        void set_default_command(console_function in_function);

        void print(string const& in_text);

    private:
        friend void console_functions::inspect_variable(vector< string >::type const& in_args);
        friend void console_functions::quit(cpac::vector< cpac::string >::type const& in_args);
        friend void console_functions::set_variable(cpac::vector< cpac::string >::type const& in_args);

    public:
        virtual void render();

        void parse_command_line(string const& in_command);

    private:
        vector< string >::type     _command_buffer;
        console_function           _default_command;

        map< string, console_function >::type _functions;
        map< string, variable >::type _variables;

    protected:
        vector< string >::type     _text_buffer;
        string                     _command_line;
    };

    extern console g_console;
}